Skip to content

fix(cli): derive repo log levels#407

Merged
moncher-dev merged 3 commits into
mainfrom
feat/386-repo-logs-level
Jun 22, 2026
Merged

fix(cli): derive repo log levels#407
moncher-dev merged 3 commits into
mainfrom
feat/386-repo-logs-level

Conversation

@moncher-dev

@moncher-dev moncher-dev commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

TL;DR

gh-symphony repo logs --level now filters historical structured events by a derived log level instead of reading the nonexistent event.level field. It also treats worker turn failures as errors, rejects unsupported level values, and reports empty filtered results without implying only --level was responsible.

변경 지점 다이어그램

repo logs args -> validate --level -> run event NDJSON -> derive level from event discriminator -> apply filters -> format matching events or print no-match notice

여기부터 보세요

  • packages/cli/src/commands/logs.ts: validates --level, derives error | warn | info from event type, includes turn_failed in error-level output, and reports no matches for filtered log views.
  • packages/cli/src/commands/logs.test.ts: covers scanned logs, explicit --run, warning/error filters, turn_failed, unsupported levels, and no-match notice behavior.
  • .changeset/repo-logs-level-filter.md: patch changeset for the user-visible CLI behavior change.

위험 & 롤백

Risk is low: the change is limited to CLI read/filter behavior for existing event logs and does not alter event schema or append sites. Roll back by reverting the CLI helper, tests, and changeset in this PR.

변경 파일

  • .changeset/repo-logs-level-filter.md
  • packages/cli/src/commands/logs.ts
  • packages/cli/src/commands/logs.test.ts

Evidence

  • pnpm --filter @gh-symphony/cli test -- src/commands/logs.test.ts — passed, 10 tests
  • pnpm lint — passed
  • pnpm test — passed
  • pnpm typecheck — passed
  • pnpm build — passed

Issues — Closed #386

Closes #386

머지 후/사람 확인

  • Confirm gh-symphony repo logs --level error shows failed historical events in a real runtime directory.
  • Confirm no adjacent repo logs filters regress for operators.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 39.09% 28341 / 72495
🔵 Statements 39.09% 28341 / 72495
🔵 Functions 71.61% 1385 / 1934
🔵 Branches 76.37% 5797 / 7590
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/cli/src/commands/logs.ts 73.43% 71.59% 100% 73.43% 34-35, 37-39, 76-83, 86-89, 108-110, 116-160, 194, 199, 202-204, 277, 295-296
Generated in workflow #285 for commit 25f49a5 by the Vitest Coverage Report Action

@moncher-dev moncher-dev changed the title fix(cli): derive repo log levels for filtering fix(cli): derive repo log levels Jun 22, 2026
@moncher-dev moncher-dev marked this pull request as ready for review June 22, 2026 13:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85f848f3ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +225 to +228
case "run-failed":
case "worker-error":
case "hook-failed":
return "error";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include turn_failed in error-level filtering

When a worker turn fails, the orchestrator writes a structured run event with event: "turn_failed" (checked packages/orchestrator/src/service.ts:2347-2351), but this derived-level switch falls through to info for that event. In runs where the failure recorded in events.ndjson is a turn failure, gh-symphony repo logs --level error hides the failure and may report no error matches, which defeats the new level filter for a real runtime failure. Add turn_failed to the error cases.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in aaa1855: turn_failed is now classified as error, and the CLI logs regression test asserts --level error includes turn_failed output.

@hojinzs hojinzs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Project #14 watchdog review: no blocking findings found.

Verification performed:

Note: I am approving the PR review, but I am not moving the project item to Land because this changes CLI runtime behavior under packages/cli/**, so it does not pass the low-risk Land gate for automatic landing.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes gh-symphony repo logs --level filtering by deriving a log level from the structured event discriminator (since events don’t have an event.level field) and improves CLI UX by emitting a notice when --level yields no matches. This is primarily a CLI/observability consumption change (it does not alter the event schema or event writers).

Changes:

  • Derive error | warn | info from event.event when applying --level filtering in repo logs.
  • Track whether any events matched the --level filter and print a no-match notice instead of returning empty output.
  • Add tests covering derived-level filtering and the no-match notice behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
packages/cli/src/commands/logs.ts Derives a LogLevel from event discriminators, applies it to --level filtering, and reports no-match cases.
packages/cli/src/commands/logs.test.ts Adds regression tests for derived --level filtering and no-match notice output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +223 to 235
function getLevel(event: LoggedEvent): LogLevel {
switch (event.event) {
case "run-failed":
case "worker-error":
case "hook-failed":
return "error";
case "run-suppressed":
case "run-retried":
return "warn";
default:
return "info";
}
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in aaa1855: added turn_failed to the derived error-level cases and covered it in logs.test.ts.

Comment on lines 79 to 83
try {
const content = await readFile(eventsPath, "utf8");
const lines = content.trim().split("\n").filter(Boolean);
let matchedEvents = 0;
for (const line of lines) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in aaa1855: --level is validated once before reading logs. Unsupported values now fail fast with Unknown --level "<value>". Valid values: error, warn, info. and exit code 1.

Comment on lines 155 to 157
let foundRuns = false;
let matchedEvents = 0;
const events: LoggedEvent[] = [];

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in aaa1855: validation now happens once before both the --run path and scan path, so unsupported levels never reach either filtering path.

Comment on lines +333 to +334
});

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in aaa1855: added a regression test for --run <id> --level error where the run has events but none match the derived level.

@hojinzs hojinzs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approve — completeness verification (watchdog review)

Requirements coverage (issue #386): Fully met.

  • getLevel now derives error | warn | info from the event discriminator using exactly the mapping proposed in the issue (error ← run-failed/worker-error/hook-failed, warn ← run-suppressed/run-retried, info otherwise), and the existing --level filter consumes it (logs.ts:87, logs.ts:175). The bug — filtering on the nonexistent event.level — is gone.
  • The "matches nothing" gap is closed: a No events matched --level <x>. notice is written to stderr instead of silent empty output, in both the --run path and the scan path.
  • Adds the --level test coverage the issue flagged as missing.

Smoke test (black-box, built CLI against a synthetic .runtime/orchestrator):

  • repo logs --level error → only run-failed
  • repo logs --level warn → only run-retried
  • repo logs --level inforun-started + run-completed
  • no filter → all events ✓
  • run with only info events + --level errorNo events matched --level error. on stderr (no silent empty) ✓
  • --run <id> --level warn path → filters correctly ✓
  • unknown level (--level debug) → graceful notice, no crash ✓

Quality: pnpm --filter @gh-symphony/cli test (8/8 in logs.test.ts), typecheck, and lint all clean locally; CI Test + Container Smoke green. Scope is appropriately minimal (CLI read/filter only — no event-schema change), avoiding over-engineering. Two non-blocking nits left inline (level-value validation, notice attribution).

No blockers. Approving.


Generated by Claude Code


function getLevel(event: LoggedEvent): string | undefined {
return typeof event.level === "string" ? event.level : undefined;
function getLevel(event: LoggedEvent): LogLevel {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (non-blocking): getLevel now returns a closed error | warn | info set, but parsed.level is still an arbitrary string compared with getLevel(event) !== parsed.level. A misspelled or unsupported value (--level Error, --level debug) silently falls through to the "No events matched" notice. Since the whole point of #386 was that operators get misled by empty output, consider validating --level against the three accepted values up front and surfacing them (e.g. Unknown --level "debug". Valid values: error, warn, info.). Not required for this fix.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepted and resolved in aaa1855: --level now validates against error, warn, and info up front, with a clear error and nonzero exit for unsupported values.

Comment thread packages/cli/src/commands/logs.ts Outdated
process.stderr.write("No runs found. Start the orchestrator first.\n");
return;
}
if (parsed.level && matchedEvents === 0) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (non-blocking): matchedEvents is incremented after all three filters (project, level, issue), but the notice attributes an empty result solely to --level. So repo logs --level error --issue does/not#exist prints "No events matched --level error." even when it was the --issue filter that excluded everything. Minor wording accuracy only — fine to leave for the common case.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepted and resolved in aaa1855: the no-match notice now says No events matched the provided filters (--level <level>)., so it no longer attributes an empty result solely to the level filter when other filters are also present.

@moncher-dev

Copy link
Copy Markdown
Collaborator Author

Rework complete for the review feedback on #407.

Resolved in aaa1855:

  • Classified turn_failed as error for repo logs --level error.
  • Validated --level against error, warn, and info before scanning logs.
  • Added --run <id> --level no-match regression coverage.
  • Updated the no-match notice to refer to the provided filters instead of only --level.

Local validation passed: pnpm lint, pnpm test, pnpm typecheck, and pnpm build. CI is running on the pushed commit.

@moncher-dev moncher-dev force-pushed the feat/386-repo-logs-level branch from aaa1855 to 25f49a5 Compare June 22, 2026 17:00
@moncher-dev moncher-dev merged commit b4dda49 into main Jun 22, 2026
2 checks passed
@moncher-dev moncher-dev deleted the feat/386-repo-logs-level branch June 22, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

repo logs --level filters on a non-existent field and silently returns zero output

3 participants